home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 25 / AACD 25.iso / AACD / Magazine / Online / QMail / source / flock.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-09-10  |  6.3 KB  |  279 lines

  1. /* flock.c : flock() and amiga_flock()
  2.  
  3.  * flock()
  4.  
  5.  * Attempts to emulate flock().
  6.  * Lock files (filename.lock) are used for this.
  7.  * open() does not support bloking behaviour, so if non-blocking behaviour
  8.  * is not requested, an attempt will be made every second until locking
  9.  * is successful.
  10.  * ChangeMode() does not support blocking behaviour, so if non-blocking
  11.  * behaviour is not requested, an attempt will be made every second until
  12.  * locking is successful.
  13.  */
  14.  
  15. #define _KERNEL
  16. #define _INTERNAL_FILE
  17.  
  18. #include <dos/dos.h>
  19. #include <dos/dosextens.h>
  20.  
  21. #include <stdlib.h>
  22. #include <unistd.h>
  23. #include <sys/file.h>
  24. #include <errno.h>
  25.  
  26. #include <proto/dos.h>
  27.  
  28. static fd_set locked;
  29. static fd_set shared;
  30. static int lock_fd [FD_SETSIZE];
  31.  
  32. int amiga_flock(int fd, int flags);
  33. static int create_lock (int fd, BOOL exclusive, BOOL blocking);
  34. static int create_lock_file (char *lockname, BOOL exclusive);
  35.  
  36. int flock (int fd, int flags)
  37. {
  38.   /* Create a new shared lock or turn an existing exclusive lock into
  39.    * a shared one.
  40.    */
  41.   if (flags & LOCK_SH)
  42.   {
  43.     /* Change existing shared lock. */
  44.     if (FD_ISSET (fd, &locked))
  45.     {
  46.       if (FD_ISSET (fd, &shared))
  47.         return 0;
  48.       else
  49.       {
  50.         if (-1 != amiga_flock (lock_fd [fd], ((flags & LOCK_NB) | LOCK_SH)))
  51.         {
  52.           FD_SET (fd, &shared);
  53.           return (0);
  54.         }
  55.         else
  56.           return (-1);
  57.       }
  58.     }
  59.     /* Create new shared lock. */
  60.     else
  61.     {
  62.       if (-1 != (lock_fd [fd] = create_lock (fd, FALSE, !(flags & LOCK_NB))))
  63.       {
  64.         FD_SET (fd, &locked);
  65.         FD_SET (fd, &shared);
  66.         return (0);
  67.       }
  68.       else
  69.         return (-1);
  70.     }
  71.   }
  72.  
  73.   /* Create a new exclusive lock or turn an existing shared lock into
  74.    * an exclusive one.
  75.    */
  76.   if (flags & LOCK_EX)
  77.   {
  78.     /* Change existing shared lock. */
  79.     if (FD_ISSET (fd, &locked))
  80.     {
  81.       if (! FD_ISSET (fd, &shared))
  82.         return 0;
  83.       else
  84.       {
  85.         if (-1 != amiga_flock (lock_fd [fd], ((flags & LOCK_NB) | LOCK_EX)))
  86.         {
  87.           FD_CLR (fd, &shared);
  88.           return (0);
  89.         }
  90.         else
  91.           return (-1);
  92.       }
  93.     }
  94.     /* Create new exclusive lock. */
  95.     else
  96.     {
  97.       if (-1 != (lock_fd [fd] = create_lock (fd, TRUE, !(flags & LOCK_NB))))
  98.       {
  99.         FD_SET (fd, &locked);
  100.         FD_CLR (fd, &shared);
  101.         return (0);
  102.       }
  103.       else
  104.         return (-1);
  105.     }
  106.   }
  107.  
  108.   /* Release an existing lock. */
  109.   if (flags & LOCK_UN)
  110.   {
  111.     if (FD_ISSET (fd, &locked))
  112.     {
  113.       close (lock_fd [fd]);
  114.       FD_CLR (fd, &locked);
  115.     }
  116.     return (0);
  117.   }
  118.  
  119.   /* No valid flags specified? */
  120.   errno = EINVAL;
  121.   return (-1);
  122. }
  123.  
  124. static int create_lock (int fd, BOOL exclusive, BOOL blocking)
  125. {
  126.   struct file *fp;
  127.   char *lockname;
  128.   int lock_fd;
  129.  
  130.   fp = (struct file *) fcntl (fd, F_EXTERNALIZE, 0);
  131.   if (fp == (struct file *) -1 || fp == NULL)
  132.     return (-1);
  133.  
  134.   if (! (( lockname = malloc (strlen (fp->f_name) + 6) )) )
  135.   {
  136.     errno = ENOMEM;
  137.     return (-1);
  138.   }
  139.  
  140.   strcpy (lockname, fp->f_name);
  141.   strcat (lockname, ".lock");
  142.   errno = 0;
  143.  
  144.   lock_fd = create_lock_file (lockname, exclusive);
  145.  
  146.   /* If the lock failed: exit with error if non-blocking, or keep
  147.    * retrying until success (or unrelated failure) if blocking.
  148.    */
  149.   while (errno == EWOULDBLOCK && blocking)
  150.   {
  151.     errno = 0;
  152.     sleep (1);
  153.     lock_fd = create_lock_file (lockname, exclusive);
  154.   }
  155.  
  156.   return (lock_fd);
  157. }
  158.  
  159. static int create_lock_file (char *lockname, BOOL exclusive)
  160. {
  161.   struct file *lockfile;
  162.   int temp_errno;
  163.   int lock_fd;
  164.  
  165.   /* Attempt to open the lock file. To avoid deadlock (multiple tasks
  166.    * managing to open() before at least one amiga_lock()s) on exclusive
  167.    * locks, add O_EXCL when exclusive mode is requested.
  168.    */
  169.   if (-1 == (lock_fd = open (lockname, O_CREAT /* | (exclusive ? O_EXCL : 0) */ )))
  170.   {
  171.     if (errno == EEXIST)
  172.       errno = EWOULDBLOCK;
  173.     return (-1);
  174.   }
  175.  
  176.   /* The lock file should be deleted when it is closed. */
  177.   lockfile = (struct file *) fcntl (lock_fd, F_EXTERNALIZE);
  178.   if (lockfile == (struct file *) -1 || lockfile == NULL)
  179.   {
  180.     temp_errno = errno;
  181.     close (lock_fd);
  182.     errno = temp_errno;
  183.     return (-1);
  184.   }
  185.   lockfile->f_flags |= FUNLINK;
  186.  
  187.   /* Change file handle to exclusive mode if requested. */
  188.   if (exclusive && !errno)
  189.   {
  190.     if (-1 == (amiga_flock (lock_fd, LOCK_EX | LOCK_NB)))
  191.     {
  192.       temp_errno = errno;
  193.       close (lock_fd);
  194.       errno = temp_errno;
  195.       return (-1);
  196.     }
  197.   }
  198.   return (lock_fd);
  199. }
  200.  
  201. /* amiga_flock()
  202.  
  203.  * Similar to flock(), but uses mandatory Amiga file locks instead of
  204.  * advisory locks.
  205.  * Note that because of this, there will always be at least a shared lock
  206.  * on the file with this implementation.
  207.  * ChangeMode() does not support blocking behaviour, so if non-blocking
  208.  * behaviour is not requested, an attempt will be made every second until
  209.  * locking is successful.
  210.  */
  211.  
  212. /*
  213. #define DEBUG_VERSION
  214. #include <kprintf.h>
  215.  
  216. char __flock_errorbuf[256];
  217. char __flock_fnamebuf[1024];
  218. char __flock_tnamebuf[1024];
  219. */
  220.  
  221. /* FIXME ix_amiga.h doesn't declare this function. */
  222. /* #include <ix_amiga.h> */
  223. BPTR __amiga_filehandle (int fd);
  224.  
  225. int amiga_flock (int fd, int flags)
  226. {
  227.   BPTR filehandle;
  228.  
  229.   /* FIXME __amiga_filehandle() doesn't always set errno on failure :-( */
  230.   errno = 0;
  231.  
  232.   /* Get the filehandle (if any) associated with the file descriptor */
  233.   if ((filehandle = __amiga_filehandle (fd)))
  234.   {
  235.     ULONG newmode;
  236.     BOOL success;
  237.  
  238.     if ((flags & LOCK_SH) || (flags & LOCK_UN))
  239.       newmode = SHARED_LOCK;
  240.     else if (flags & LOCK_EX)
  241.       newmode = EXCLUSIVE_LOCK;
  242.     else
  243.       return (0);
  244.  
  245.     if (flags & LOCK_NB)
  246.     {
  247.       if ((success = ChangeMode (CHANGE_FH, filehandle, newmode)))
  248.         return (0);
  249.       else
  250.       {
  251.         errno = EWOULDBLOCK;
  252. /*
  253.         Fault (IoErr(), NULL, __flock_errorbuf, 256);
  254.         NameFromFH (filehandle, __flock_fnamebuf, 1024);
  255.         GetProgramName (__flock_tnamebuf, 1024);
  256.         KPrintF ("%s: ChangeMode (%s,%ld) failure: %s\n",
  257.                  __flock_tnamebuf, __flock_fnamebuf , newmode,
  258.                  __flock_errorbuf);
  259. */
  260.         return (-1);
  261.       }
  262.     }
  263.     else
  264.     /* Blocking behaviour - loop (with delay) until success. */
  265.     {
  266.       while (! ChangeMode (CHANGE_FH, filehandle, newmode))
  267.         sleep (1);
  268.       return (0);
  269.     }
  270.   }
  271.   /* Getting the file handle failed. */
  272.   else
  273.   {
  274.     if (errno != EBADF)
  275.       errno = EINVAL;
  276.     return (-1);
  277.   }
  278. }
  279.